package net.xdclass.service.common.impl;

import cn.hutool.core.util.IdUtil;
import io.minio.GetPresignedObjectUrlArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.http.Method;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import net.xdclass.config.FileUploadExecutor;
import net.xdclass.config.MinIoConfig;
import net.xdclass.enums.BizCodeEnum;
import net.xdclass.exception.BizException;
import net.xdclass.service.common.FileService;
import net.xdclass.util.CustomFileUtil;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.concurrent.*;

@Service
@Slf4j
public class FileServiceImpl implements FileService {

    @Resource
    private MinIoConfig minIoConfig;

    @Resource
    private MinioClient minioClient;

    @Resource
    private FileUploadExecutor fileUploader;

    /**
     * 获取文件名称
     * 生成随机名称
     * 文件上传
     * 拼接路径返回
     *
     * @param file
     * @return
     */
    @Override
    public String upload(MultipartFile file) {
        String filename = CustomFileUtil.getFilename(file.getOriginalFilename());

        //上传 TODO 异步上传，改成线程池上传

        try {
            // 提交任务到线程池
            Future<?> future = fileUploader.getExecutorService().submit(() -> upload(file, filename));
            // 如果需要等待上传完成，可以使用 future.get()，但这将阻塞主线程直到任务完成
        } catch (Exception e) {
            // 处理提交任务时可能出现的异常
            e.printStackTrace();
            // 返回错误信息或抛出自定义异常
        }

        // 返回文件路径，注意此时文件可能还未上传完毕
        return minIoConfig.getEndpoint() + "/" + minIoConfig.getBucketName() + "/" + filename;
    }

    @Override
    /**
     * 根据远程文件路径生成临时访问文件的URL
     * 该方法主要用于在安全的时间范围内，提供文件的直接访问链接，便于第三方服务或用户临时访问文件
     *
     * @param remoteFilePath 远程文件路径，表示文件在对象存储中的唯一位置
     * @return 临时访问文件的URL字符串，该URL具有时效性，过期后无法访问文件
     */
    public String getTempAccessFileUrl(String remoteFilePath) {
        try {
            // 提取文件名，用于MinIO对象路径
            String fileName = remoteFilePath.substring(remoteFilePath.lastIndexOf("/") + 1);

            // 创建获取预签名URL的参数对象
            GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder()
                    .bucket(minIoConfig.getBucketName()) // 设置存储桶名称
                    .object(fileName)                    // 设置对象名称，即文件名
                    .method(Method.GET)                  // 设置HTTP方法为GET
                    .expiry(1, TimeUnit.MINUTES)         // 设置URL有效期为1分钟
                    .build();

            // 通过MinIO客户端获取预签名的Object URL，并返回
            return minioClient.getPresignedObjectUrl(args);
        } catch (Exception e) {
            // 记录获取临时文件访问链接异常日志
            log.error("获取临时文件访问链接失败", e);
            // 抛出运行时异常，提示获取临时文件访问链接失败
            throw new RuntimeException("获取临时文件访问链接失败");
        }
    }

    /**
     * 根据远程文件地址读取内容到本地创建临时文件
     * @param remoteFilePath
     * @return
     */
    @Override
    public String copyRemoteFileToLocalTempFile(String remoteFilePath) {

        String localTempFile = System.getProperty("user.dir")+ File.separator+"static"+File.separator+ IdUtil.simpleUUID() +"."+ CustomFileUtil.getSuffix(remoteFilePath);
        CustomFileUtil.mkdir(localTempFile);
        try {
            String tempFileUrl = getTempAccessFileUrl(remoteFilePath);
            // 从远程URL获取输入流
            URL url = new URL(tempFileUrl);
            InputStream inputStream = url.openStream();
            // 将输入流的内容持久化到磁盘上的文件
            Path localFile = Path.of(localTempFile);
            Files.copy(inputStream, localFile, StandardCopyOption.REPLACE_EXISTING);
            // 关闭资源
            inputStream.close();
            return localFile.toFile().getPath();
        }catch (Exception e){
            throw new BizException(BizCodeEnum.FILE_REMOTE_DOWNLOAD_FAILED,e);
        }

    }


    private void upload(MultipartFile file, String fileName) {
        if (file == null || file.getSize() == 0) {
            throw new RuntimeException("文件为空");
        } else {
            try {
                InputStream inputStream = file.getInputStream();
                PutObjectArgs putObjectArgs = PutObjectArgs.builder().bucket(minIoConfig.getBucketName()).object(fileName)
                        .stream(inputStream, file.getSize(), -1)
                        .contentType(file.getContentType()).build();
                minioClient.putObject(putObjectArgs);
            } catch (Exception e) {
                throw new RuntimeException("文件上传失败");
            }
        }
    }
}
